﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Threading;

namespace HovisPresent
{
	public partial class ViewForm : Form
	{
		private MainWindow mainWindow;
		private String script=null;
		private Font scriptFont;
		private Rectangle scriptRectangle;
		private SizeF calculatedScriptSize;
		private bool scriptHidden;
		public const int PreviewZoomplayerPort = 4021;
		private bool isPresenterView=true;
		private bool messageOnCurrent = false;
		private bool isFullScreen = true;

		private DSVideo playerWindow;
		public DSVideo VideoPlayer { get { return playerWindow; } }

		public ViewForm() {
			InitializeComponent();
		}

		/// <summary>
		/// Initializes a new instance of the <see cref="ViewForm"/> class.
		/// </summary>
		/// <param name="mainWindow">The main window.</param>
		/// <param name="presenterView">if set to <c>true</c> [presenter view].</param>
		/// <param name="playerWindowName">Name of the player window.</param>
		public ViewForm(MainWindow mainWindow, bool presenterView,string playerWindowName) {
			isPresenterView = presenterView;
			InitializeComponent();
			this.mainWindow = mainWindow;
			scriptFont = mainWindow.ScriptFont;
			PreviewKeyDown += VideoWindow_PreviewKeyDown;
			playerWindow = new DSVideo(!isPresenterView);
			playerWindow.Name = playerWindowName;
		}

		/// <summary>
		/// Handles the Load event of the Form control.
		/// </summary>
		/// <param name="sender">The source of the event.</param>
		/// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
		private void Form_Load(object sender, EventArgs e) {
			nextPicture.Visible = nextLabel.Visible = isPresenterView;
			currentLabel.Visible = isPresenterView;
			currentPicture.Visible = false;

			netDisconnectedIcon.BringToFront();
			currentLabel.BringToFront();
			nextLabel.BringToFront();

			Text = isPresenterView ? "Presenter Window" : "Presentation";

			playerWindow.PreviewKeyDown += VideoWindow_PreviewKeyDown;
			
			if (isPresenterView)
				playerWindow.DSVideoMessage += new DSVideoMessageEventHandler(DSVideoMessageHandler);
			Controls.Add(playerWindow);

			ResizeChildren();
		}

		//
		// Labels show messages and need to be reset on each new slide
		//
		private void ResetLabels() {
			if (isPresenterView)
				nextLabel.Text = "Next...";
			ResetCurrentLabel();
		}

		//
		// Reset the current label to blank or "current" depending on mode
		//
		private void ResetCurrentLabel() {
			if (!messageOnCurrent)
				currentLabel.Text = isPresenterView ? "Current" : "";
		}

		/// <summary>
		/// handles the DSVideoEvent
		/// </summary>
		/// <param name="sender">The sender.</param>
		/// <param name="e">The <see cref="HovisPresent.DSVideoMessageEventArgs"/> instance containing the event data.</param>
		public void DSVideoMessageHandler(object sender, DSVideoMessageEventArgs e) {
			if (e.Status == DSVideoMessageStatus.Error && e.Exception != null)
				Console.WriteLine("Error Exception from {0}\r\n{1}", e.Source, e.Exception);
			SignalErrorOnCurrent(e.Source, e.Message);
		}

		/// <summary>
		/// Signals the error on current label
		/// </summary>
		/// <param name="source">The source.</param>
		/// <param name="error">The error.</param>
		public void SignalErrorOnCurrent(string source, string error) {
			messageOnCurrent = true;
			currentLabel.Text += "\r\n" + source + " : " + error;
		}

		/// <summary>
		/// Clears the on screen notifications.
		/// </summary>
		public void ClearNotifications() {
			messageOnCurrent = false;
			ResetLabels();
		}

		/// <summary>
		/// Changes the slide to specific files.
		/// </summary>
		/// <param name="currentFilename">The current filename. (relative to document path)</param>
		/// <param name="nextFilenameOrPreview">The next filename or preview. (relative to document path)</param>
		/// <param name="script">The script.</param>
		/// <param name="defaultHidden">if set to <c>true</c> [default hidden].</param>
		public void ChangeSlide(string currentFilename, string nextFilenameOrPreview, string script, bool defaultHidden) {
			currentFilename = mainWindow.Document.Directory + "\\" + currentFilename;
			nextFilenameOrPreview = mainWindow.Document.Directory + "\\" + nextFilenameOrPreview;

			if (isPresenterView && scriptHidden != defaultHidden) {
				scriptHidden = defaultHidden;
				ResizeChildren();
			}

			playerWindow.StopWithWait();
			startedTime.Stop();

			if (Slide.IsFilenameAnImage(currentFilename)) {
				playerWindow.Hide();
				try {
					currentPicture.Load(currentFilename);
					currentPicture.Show();
				} catch (Exception e) { // can't display it
					playerWindow.SignalError("Error Loading Image\r\n" + e.Message, null);
					currentPicture.Hide();
				}
			} else {
				currentPicture.Hide();
				playerWindow.PlayFile(currentFilename);
				playerWindow.Show();
				if (isPresenterView && !messageOnCurrent)
					currentLabel.Text = "Current - started video";
				startedTime.Start();
			}

			//
			// Load next slide on presenter view
			if (isPresenterView) {
				ChangeScript(script);
				try {
					nextPicture.Load(nextFilenameOrPreview);
					nextPicture.Show();
				} catch (Exception) {
					// can't display it? what can we do?
					nextPicture.Hide();
				}
			}
		}

		/// <summary>
		/// Stop and wait for the graph to stop.
		/// </summary>
		public void StopWithWait() {
			playerWindow.StopWithWait();
		}

		/// <summary>
		/// Issues stop, but doesnt wait to confirm
		/// </summary>
		public void Stop() {
			playerWindow.Stop();
			if (!isPresenterView)
				currentPicture.Hide();
		}

		/// <summary>
		/// Handles the FormClosing event of the VideoWindow control.
		/// </summary>
		/// <param name="sender">The source of the event.</param>
		/// <param name="e">The <see cref="System.Windows.Forms.FormClosingEventArgs"/> instance containing the event data.</param>
		private void VideoWindow_FormClosing(object sender, FormClosingEventArgs e) {
			StopWithWait();
		}

		/// <summary>
		/// Handles the SizeChanged event of the VideoWindow control.
		/// </summary>
		/// <param name="sender">The source of the event.</param>
		/// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
		private void VideoWindow_SizeChanged(object sender, EventArgs e) {
			ResizeChildren();
		}

		/// <summary>
		/// Resizes the children.
		/// </summary>
		private void ResizeChildren() {
			if (isPresenterView)
				ResizePresenterChildren();
			else
				ResizePresentationChildren();

			currentLabel.Top = playerWindow.Top;
			currentLabel.Left = playerWindow.Left;
			currentLabel.MaximumSize = playerWindow.Size;

			playerWindow.Invalidate();
		}

		/// <summary>
		/// Resizes the children in presentation view
		/// </summary>
		private void ResizePresentationChildren() {
			currentPicture.Size = playerWindow.Size = ClientSize;
			currentPicture.Top = playerWindow.Top = 0;
			currentPicture.Left = playerWindow.Left = 0;
		}

		/// <summary>
		/// Resizes the children in presenter view
		/// </summary>
		private void ResizePresenterChildren() {
			if (scriptHidden) {
				Size half = new Size(ClientRectangle.Width / 2, ClientRectangle.Height);
				currentPicture.Size = playerWindow.Size = nextPicture.Size = half;
				currentPicture.Top = playerWindow.Top = nextPicture.Top = 0;
				currentPicture.Left = playerWindow.Left = 0;
				nextPicture.Left = half.Width;				
			} else {
				if (mainWindow.PresenterLayout == PresenterWindowLayout.ScriptBottom) {
					playerWindow.Size = nextPicture.Size = new Size(ClientRectangle.Width / 2, ClientRectangle.Height / 3);
					playerWindow.Left = 0;
					nextPicture.Left = ClientRectangle.Width - nextPicture.Size.Width;
					playerWindow.Top = nextPicture.Top = 0;
					playerWindow.Left = 0;
					currentPicture.Location = playerWindow.Location;
					currentPicture.Size = playerWindow.Size;

					scriptRectangle = new Rectangle(0, playerWindow.Size.Height, ClientRectangle.Width, ClientRectangle.Height - playerWindow.Size.Height);
					Invalidate(scriptRectangle);
					RecalcScriptFontSize();
				} else {
					Size miniWindow = new Size(ClientRectangle.Width / 3, ClientRectangle.Height / 2);
					playerWindow.Size = nextPicture.Size = miniWindow;
					playerWindow.Left = nextPicture.Left = ClientRectangle.Width - nextPicture.Size.Width;
					playerWindow.Top = 0;
					nextPicture.Top = miniWindow.Height;
					currentPicture.Location = playerWindow.Location;
					currentPicture.Size = playerWindow.Size;

					scriptRectangle = new Rectangle(0, 0, ClientRectangle.Width - miniWindow.Width, ClientRectangle.Size.Height);
					Invalidate(scriptRectangle);
					RecalcScriptFontSize();
				}
			}

			nextLabel.Top = nextPicture.Top;
			nextLabel.Left = nextPicture.Left;
			nextLabel.MaximumSize = nextPicture.Size;
		}

		/// <summary>
		/// Handles the Paint event of the VideoWindow control.
		/// </summary>
		/// <param name="sender">The source of the event.</param>
		/// <param name="e">The <see cref="System.Windows.Forms.PaintEventArgs"/> instance containing the event data.</param>
		private void VideoWindow_Paint(object sender, PaintEventArgs e) {
			Graphics g = e.Graphics;

			if (string.IsNullOrEmpty(script) || scriptFont==null || scriptHidden)
				return;

// debug: outline the script area
//			Rectangle r = new Rectangle(scriptRectangle.Left,scriptRectangle.Top,scriptRectangle.Width-1,scriptRectangle.Height-1);
//			g.DrawRectangle(Pens.Blue, r);

			int heightOffset = (int) ((scriptRectangle.Height - calculatedScriptSize.Height) / 2);
			Rectangle textRect = new Rectangle(scriptRectangle.Left, scriptRectangle.Top + heightOffset,
												(int)scriptRectangle.Width, (int)calculatedScriptSize.Height);
// debug: outline where we are going to draw script			
//			g.DrawRectangle(Pens.Red, textRect);

			g.DrawString(script, scriptFont, Brushes.White, textRect);
		}

		/// <summary>
		/// Changes the script.
		/// </summary>
		/// <param name="s">The s.</param>
		private void ChangeScript(string s) {
			script = s;
			RecalcScriptFontSize();
			Invalidate(scriptRectangle);
		}

		/// <summary>
		/// Recalcs the size of the script font.
		/// </summary>
		private void RecalcScriptFontSize() {
			Graphics g = CreateGraphics();

			SizeF defaultFontSize = g.MeasureString(script, mainWindow.ScriptFont, scriptRectangle.Width);
			if (defaultFontSize.Height < scriptRectangle.Height) {
				// fits with default font
				if (scriptFont != mainWindow.ScriptFont) {
					scriptFont.Dispose();
					scriptFont = mainWindow.ScriptFont;
				}
				calculatedScriptSize = defaultFontSize;
				return;
			}

			if (scriptFont != mainWindow.ScriptFont)
				scriptFont.Dispose();

			// reduce font size till it fits
			float fontSize = mainWindow.ScriptFont.Size;
			Font tempFont = null;
			SizeF tempScriptSize = SizeF.Empty;
			while (true) {
				fontSize = fontSize * 0.9f;

				// still not fitting, oh well
				if (fontSize < 6) {
					break;
				}

				if (tempFont != null)
					tempFont.Dispose();

				tempFont = new Font(mainWindow.ScriptFont.FontFamily, fontSize, mainWindow.ScriptFont.Style);
				tempScriptSize = g.MeasureString(script, tempFont, scriptRectangle.Width);
				if (tempScriptSize.Height < scriptRectangle.Height)
					break;
			}

			scriptFont = tempFont;
			calculatedScriptSize = tempScriptSize;

			g.Dispose();
		}

		/// <summary>
		/// Sets the script hidden flag
		/// </summary>
		/// <param name="hidden">if set to <c>true</c> [hidden].</param>
		public void SetScriptHidden( bool hidden ) {
			scriptHidden = hidden;
			if (playerWindow!=null)
				ResizeChildren();
		}

		/// <summary>
		/// Handles the Tick event of the startedTime control.
		/// </summary>
		/// <param name="sender">The source of the event.</param>
		/// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
		private void startedTime_Tick(object sender, EventArgs e) {
			startedTime.Stop();

			if (!messageOnCurrent)
				ResetCurrentLabel();
		}

		/// <summary>
		/// Handles the PreviewKeyDown event of the VideoWindow control.
		/// </summary>
		/// <param name="sender">The source of the event.</param>
		/// <param name="e">The <see cref="System.Windows.Forms.PreviewKeyDownEventArgs"/> instance containing the event data.</param>
		private void VideoWindow_PreviewKeyDown(object sender, PreviewKeyDownEventArgs e) {
			mainWindow.HandleKeys(sender, e.KeyCode);
		}

		/// <summary>
		/// Shows the specified fullscreen.
		/// </summary>
		/// <param name="fullscreen">if set to <c>true</c> [fullscreen].</param>
		/// <param name="m">The m.</param>
		public void Show(bool fullScreen,Monitor m) {
			FormBorderStyle = fullScreen ? FormBorderStyle.None : FormBorderStyle.Sizable;
			
			VideoPlayer.SetMonitor((int)m);

			Screen screen = Screen.AllScreens[(int)m];

			if (fullScreen) {
				Location = screen.Bounds.Location;
				Size = screen.Bounds.Size;
			} else {
				if (isFullScreen) {
					Top = Left = isPresenterView ? 100 : 50;
					Size = new Size(screen.Bounds.Width / 2, screen.Bounds.Height / 2);
				}				
			}

			Show();
		}

		private delegate void ShowNetDisconnectedDelegate(bool disconnected);

		/// <summary>
		/// Shows the net disconnected icon
		/// </summary>
		/// <param name="disconnected">if set to <c>true</c> [disconnected].</param>
		/// <returns></returns>
		public void ShowNetDisconnected(bool disconnected) {
			if (netDisconnectedIcon.InvokeRequired) 
				netDisconnectedIcon.BeginInvoke( new ShowNetDisconnectedDelegate(ShowNetDisconnected), new object[] {disconnected} );
			else
				netDisconnectedIcon.Visible = disconnected;
		}

	}
}
